home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Chat & Communication / Digsby build 37 / digsby_setup.exe / lib / mail / gmail.pyo (.txt) < prev    next >
Python Compiled Bytecode  |  2008-10-13  |  16KB  |  475 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. from __future__ import with_statement
  5. import re
  6. import time
  7. import urllib2
  8. import cookielib
  9. import threading
  10. from threading import Lock
  11. from urllib import urlencode
  12. from datetime import datetime
  13. from traceback import print_exc
  14. from util.net import UrlQuery, GetDefaultHandlers, WebFormData
  15. from util import threaded, scrape_clean, traceguard, EmailAddress, Storage
  16. from mail import Email
  17. from common import pref
  18. from common.emailaccount import EmailAccount
  19. from logging import getLogger
  20. log = getLogger('gmail')
  21. info = log.info
  22.  
  23. class Gmail(EmailAccount):
  24.     protocol = 'gmail'
  25.     baseAuthUrl = 'https://www.google.com'
  26.     authUrl = '/accounts/ClientLogin'
  27.     tokenUrl = '/accounts/IssueAuthToken'
  28.     messageIdMatcher = re.compile('message_id=([a-z0-9]+?)&')
  29.     jsredirectMatcher = re.compile('location\\.replace\\("(.*)"\\)')
  30.     default_domain = 'gmail.com'
  31.     
  32.     def __init__(self, **k):
  33.         EmailAccount.__init__(self, **k)
  34.         self.token = ''
  35.         self.token_lock = threading.RLock()
  36.         self.datatoken = ''
  37.         self.updated_emails = None
  38.         self.updated_count = None
  39.         self.update_lock = Lock()
  40.         self.emailaddress = EmailAddress(self.name, 'gmail.com')
  41.         if self.emailaddress.domain in ('gmail.com', 'googlemail.com'):
  42.             self.baseMailUrl = '://mail.google.com/mail/'
  43.         else:
  44.             self.baseMailUrl = '://mail.google.com/a/' + self.emailaddress.domain + '/'
  45.         self.browser_http = 'https'
  46.         self.init_jar()
  47.  
  48.     can_has_preview = True
  49.     
  50.     def _reset_state(self):
  51.         self.init_jar()
  52.         self.token = self.datatoken = ''
  53.         self.sid = None
  54.         self.lsid = None
  55.  
  56.     
  57.     def browserBaseMailUrl(self):
  58.         return self.browser_http + self.baseMailUrl
  59.  
  60.     browserBaseMailUrl = property(browserBaseMailUrl)
  61.     
  62.     def internalBaseMailUrl(self):
  63.         return 'https' + self.baseMailUrl
  64.  
  65.     internalBaseMailUrl = property(internalBaseMailUrl)
  66.     
  67.     def init_jar(self):
  68.         self.jar = cookielib.CookieJar()
  69.         self.http_opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.jar), *GetDefaultHandlers())
  70.         self.http_opener.addheaders = [
  71.             ('Content-type', 'application/x-www-form-urlencoded'),
  72.             ('Cache-Control', 'no-cache')]
  73.  
  74.     
  75.     def url_token(self):
  76.         if self.web_login and self.token:
  77.             return dict(auth = self.token)
  78.         else:
  79.             return { }
  80.  
  81.     url_token = property(url_token)
  82.     
  83.     def inbox_url(self):
  84.         self.new_token()
  85.         return UrlQuery(self.browserBaseMailUrl, search = 'inbox', **self.url_token)
  86.  
  87.     inbox_url = property(inbox_url)
  88.     
  89.     def urlForEmail(self, email):
  90.         self.new_token()
  91.         return UrlQuery(self.browserBaseMailUrl, fs = '1', tf = '1', view = 'cv', search = 'all', th = str(email.id), **self.url_token)
  92.  
  93.     
  94.     def markAsRead(self, email):
  95.         EmailAccount.markAsRead(self, email)
  96.         self._do_action('read', email)
  97.  
  98.     
  99.     def archive(self, email):
  100.         EmailAccount.archive(self, email)
  101.         self._do_action('archive', email)
  102.         if pref('gmail.markive', False):
  103.             self.markAsRead(email)
  104.         
  105.  
  106.     
  107.     def delete(self, email):
  108.         EmailAccount.delete(self, email)
  109.         self._do_action('delete', email)
  110.  
  111.     
  112.     def reportSpam(self, email):
  113.         EmailAccount.reportSpam(self, email)
  114.         self._do_action('spam', email)
  115.  
  116.     
  117.     def _do_action(self, action, email):
  118.         
  119.         try:
  120.             self.gmail_at
  121.         except KeyError:
  122.             self.new_token(True)
  123.  
  124.         (url, params) = self._actionUrl(action, email.id)
  125.         response = self.webrequest(url, **params)
  126.         log.debug_s('Action %r result: %r', action, response)
  127.  
  128.     _do_action = threaded(_do_action)
  129.     
  130.     def compose(self, to = '', subject = '', body = '', cc = '', bcc = ''):
  131.         extra = dict(fs = '1', view = 'cm')
  132.         su = subject
  133.         for name in 'to su body cc bcc'.split():
  134.             if vars()[name]:
  135.                 extra[name] = vars()[name]
  136.                 continue
  137.         
  138.         self.new_token()
  139.         extra.update(self.url_token)
  140.         return UrlQuery(self.browserBaseMailUrl, **extra)
  141.  
  142.     
  143.     def _actionUrl(self, action, message_id):
  144.         action_names = dict(archive = 'rc_^i', delete = 'tr', read = 'rd', spam = 'sp')
  145.         if action not in action_names.values():
  146.             action = action_names[action]
  147.         
  148.         url = UrlQuery(self.internalBaseMailUrl, ik = '', search = 'all', view = 'tl', start = '0')
  149.         params = dict(act = action, at = self.gmail_at, vp = '', msq = '', ba = 'false', t = message_id, fs = '1')
  150.         return (url, params)
  151.  
  152.     
  153.     def send_email(self, to = '', subject = '', body = '', cc = '', bcc = ''):
  154.         log.info('sending a mail')
  155.         data = dict(nvp_bu_send = 'Send')
  156.         for name in 'to subject body cc bcc'.split():
  157.             if vars()[name]:
  158.                 data[name] = vars()[name].encode('utf-8')
  159.                 continue
  160.         
  161.         if not hasattr(self, 'sendpath'):
  162.             response = self.http_opener.open(self.internalBaseMailUrl + '?ui=html')
  163.             urlparse = urlparse
  164.             import urllib2
  165.             respurl = urlparse.urlparse(response.geturl())
  166.             
  167.             try:
  168.                 response.close()
  169.             except:
  170.                 pass
  171.  
  172.             del response
  173.             self.sendpath = respurl.path
  174.         
  175.         url = 'https://mail.google.com' + self.sendpath
  176.         
  177.         try:
  178.             at = self.gmail_at
  179.         except KeyError:
  180.             at = ''
  181.  
  182.         params = dict(at = at, v = 'b', pv = 'tl', s = 's', fv = 'b', cpt = 'c', cs = 'c')
  183.         if not self.hosted:
  184.             params.update(fv = 'b', cpt = 'c', cs = 'c')
  185.         else:
  186.             params.update(cs = 'b', s = 's')
  187.         url = UrlQuery(url, params)
  188.         response = self.webrequest(url, follow_js_redirects = True, **data)
  189.         log.info('sent a mail')
  190.         log.info('send mail success: %r', bool('Your message has been sent.' in response))
  191.         return True
  192.  
  193.     send_email = threaded(send_email)
  194.     
  195.     def _get_notifier_data(self):
  196.         return self.webrequest(url = UrlQuery(self.internalBaseMailUrl, ui = 'pb'), data = WebFormData(auth = self.datatoken))
  197.  
  198.     
  199.     def hosted(self):
  200.         domain = self.emailaddress.domain
  201.         return None if domain != 'gmail.com' else None
  202.  
  203.     hosted = property(hosted)
  204.     
  205.     def authenticate(self, task = None):
  206.         
  207.         try:
  208.             password = self._decryptedpw()
  209.             data = self.webrequest(self.baseAuthUrl + self.authUrl, data = WebFormData(Email = self.name, Passwd = password, accountType = 'HOSTED_OR_GOOGLE', service = 'mail'))
  210.         except (urllib2.HTTPError, urllib2.URLError):
  211.             e = None
  212.             if e.code == 403:
  213.                 log.warning('Invalid username or password')
  214.                 self.bad_pw(task)
  215.                 return False
  216.             else:
  217.                 log.warning('cannot authenticate gmail %s', self.name)
  218.                 log.warning('data %s' % e.fp.read())
  219.                 raise 
  220.         except:
  221.             e.code == 403
  222.  
  223.         if not data:
  224.             log.warning('No data from webrequest')
  225.             raise AssertionError('No data from webrequest')
  226.         
  227.         if data.find('Error=badauth') != -1:
  228.             log.warning('Invalid username or password')
  229.             self.bad_pw(task)
  230.             return False
  231.         else:
  232.             log.debug('received token data: %r', data)
  233.             (sid, lsid, token) = data.split('\n')[:3]
  234.             self.sid = sid
  235.             self.lsid = lsid
  236.             self.datatoken = token.split('=')[1]
  237.             d = dict([
  238.                 sid.split('='),
  239.                 lsid.split('=')])
  240.             self.new_token(True)
  241.             return True
  242.         log.warning('unknown gmail error')
  243.         raise Exception('unknown gmail error')
  244.  
  245.     
  246.     def new_token(self, at = False):
  247.         self.token_lock.__enter__()
  248.         
  249.         try:
  250.             sid = self.sid
  251.             lsid = self.lsid
  252.             d = dict([
  253.                 sid.split('='),
  254.                 lsid.split('=')])
  255.             self.getAuthToken(d, at)
  256.         finally:
  257.             pass
  258.  
  259.  
  260.     
  261.     def getAuthToken(self, d, at = False):
  262.         d.update(service = 'mail')
  263.         token = self.webrequest(self.baseAuthUrl + self.tokenUrl, **d)
  264.         if not token:
  265.             log.debug('Error getting authtoken. state is: %s', self.state)
  266.             return None
  267.         
  268.         self.token = token.strip()
  269.         if at:
  270.             self.get_gmail_at()
  271.             self.getAuthToken(d)
  272.         
  273.  
  274.     
  275.     def get_gmail_at(self):
  276.         at_token = self.token
  277.         url = self.internalBaseMailUrl + '?auth=%s' % at_token
  278.         res = self.webrequest(url, follow_js_redirects = True)
  279.         
  280.         try:
  281.             self.gmail_at
  282.         except KeyError:
  283.             log.debug('GMAIL_AT result: %r', res)
  284.  
  285.  
  286.     
  287.     def gmail_at(self):
  288.         hosted = self.hosted
  289.         at_path = None if hosted else '/mail'
  290.         return self.jar._cookies['mail.google.com'][at_path]['GMAIL_AT'].value
  291.  
  292.     gmail_at = property(gmail_at)
  293.     
  294.     def update(self):
  295.         log.info('update at %s', time.ctime(time.time()))
  296.         EmailAccount.update(self)
  297.         self.real_update(success = self.finish_update, error = self.on_error)
  298.  
  299.     
  300.     def finish_update(self, updates):
  301.         if updates is sentinel:
  302.             log.warning('two updates were running at the same time')
  303.             return None
  304.         
  305.         
  306.         try:
  307.             (updated_emails, updated_count) = updates
  308.         except (TypeError, ValueError):
  309.             log.error('Update failed for %s, assuming auth error', self.name)
  310.             return None
  311.  
  312.         log.info('%s got %d new messages %s', self, updated_count, time.ctime(time.time()))
  313.         self._received_emails(updated_emails[:25], updated_count)
  314.  
  315.     
  316.     def real_update(self):
  317.         if self.update_lock.acquire(False):
  318.             
  319.             try:
  320.                 if not self.token:
  321.                     info('no auth token yet, authenticating')
  322.                     if not self.authenticate():
  323.                         log.info('auth failed, returning None from real_update')
  324.                         return None
  325.                     
  326.                 
  327.                 info('updating Gmail account %s at %s' % (self.name, time.ctime(time.time())))
  328.                 (updated_emails, updated_count, _data) = parse_datapacks(self._get_notifier_data())
  329.                 updated_emails = chunks_to_emails(updated_emails)
  330.                 return (updated_emails, updated_count)
  331.             finally:
  332.                 self.update_lock.release()
  333.  
  334.         else:
  335.             return sentinel
  336.  
  337.     real_update = threaded(real_update)
  338.     
  339.     def webrequest(self, url, data = '', follow_js_redirects = False, **kwparams):
  340.         
  341.         try:
  342.             response = self.http_opener.open(url, data + urlencode(kwparams.items()))
  343.             resp = response.read()
  344.             if follow_js_redirects:
  345.                 match = self.jsredirectMatcher.search(resp)
  346.                 if match:
  347.                     new_url = match.groups()[0]
  348.                     response = self.http_opener.open(self.baseAuthUrl + new_url)
  349.                     resp = response.read()
  350.                 
  351.             
  352.             return resp
  353.         except (urllib2.HTTPError, urllib2.URLError):
  354.             e = None
  355.             if getattr(e, 'code', None) == 403:
  356.                 log.warning('Invalid username or password')
  357.                 self.bad_pw()
  358.                 return False
  359.             else:
  360.                 print_exc()
  361.                 import sys as sys
  362.                 print >>sys.stderr, 'url: %s' % url
  363.         except Exception:
  364.             e = None
  365.             print_exc()
  366.  
  367.         return False
  368.  
  369.  
  370. from util import utf7_to_int as u7i
  371.  
  372. def chunk_datapack(data):
  373.     data = data[1:]
  374.     (num, numbytes) = u7i(data)
  375.     data = data[numbytes:]
  376.     return (data[:num], data[num:])
  377.  
  378.  
  379. def get_chunk(data):
  380.     (type_, numbytes) = u7i(data)
  381.     data = data[numbytes:]
  382.     if type_ == 184:
  383.         (value, length_) = u7i(data)
  384.     elif type_ == 152:
  385.         (value, length_) = u7i(data)
  386.     else:
  387.         (length_, numbytes) = u7i(data)
  388.         data = data[numbytes:]
  389.     return (type_, data[:length_], data[length_:])
  390.  
  391.  
  392. def get_mid_date(data):
  393.     orig_length = len(data)
  394.     (length_, numbytes) = u7i(data)
  395.     expected_length = orig_length - length_ - numbytes
  396.     data = data[numbytes:]
  397.     (msgid, numbytes) = u7i(data)
  398.     data = data[numbytes:]
  399.     (_unknown, numbytes) = u7i(data)
  400.     data = data[numbytes:]
  401.     (time_in_ms, numbytes) = u7i(data)
  402.     data = data[numbytes:]
  403.     return (msgid, time_in_ms, data)
  404.  
  405. from collections import defaultdict
  406.  
  407. def parse_chunk(chunk):
  408.     retval = defaultdict(list)
  409.     (mid, time_in_ms, data) = get_mid_date(chunk)
  410.     retval['mid'] = mid
  411.     retval['time'] = time_in_ms
  412.     while data:
  413.         (t, v, data) = get_chunk(data)
  414.         if t == 146:
  415.             v = parse_from(v)
  416.         elif t == 184:
  417.             v = u7i(v)[0]
  418.         
  419.         retval[t].append(v)
  420.     return retval
  421.  
  422.  
  423. def chunks_to_emails(dictionaries):
  424.     return [ dict_to_email(e) for e in dictionaries ]
  425.  
  426.  
  427. def parse_datapacks(data):
  428.     retval = []
  429.     while data[0] == '\n':
  430.         (chunk, data) = chunk_datapack(data)
  431.         retval.append(parse_chunk(chunk))
  432.     num_messages = 0
  433.     (type_, numbytes) = u7i(data)
  434.     data = data[numbytes:]
  435.     if type_ == 136:
  436.         (num_messages, numbytes) = u7i(data)
  437.         data = data[numbytes:]
  438.     
  439.     return (retval, num_messages, data)
  440.  
  441.  
  442. def parse_from(from_):
  443.     retval = { }
  444.     from_ = from_[1:]
  445.     (retval['mystery_bytes1'], from_) = from_.split('\n', 1)
  446.     (length_, numbytes) = u7i(from_)
  447.     from_ = from_[numbytes:]
  448.     retval['email_addr'] = from_[:length_]
  449.     from_ = from_[length_:]
  450.     (type_, numbytes) = u7i(from_)
  451.     if type_ == 18:
  452.         from_ = from_[numbytes:]
  453.         (length_, numbytes) = u7i(from_)
  454.         from_ = from_[numbytes:]
  455.         retval['from_text'] = from_[:length_]
  456.         from_ = from_[length_:]
  457.     
  458.     retval['remainder_bytes'] = from_
  459.     return retval
  460.  
  461.  
  462. decode = lambda s: scrape_clean(s.decode('utf-8'))
  463.  
  464. def dict_to_email(d):
  465.     k = Storage(labels = 130, author = 146, subject = 162, snippet = 170, attachments = 178)
  466.     msgid = d['mid']
  467.     author_email = d[k.author][-1]['email_addr']
  468.     author_name = decode(d[k.author][-1].get('from_text', ''))
  469.     subject = decode(d[k.subject][-1])
  470.     snippet = decode(d[k.snippet][-1])
  471.     attachments = [ Storage(name = a) for a in d[k.attachments] ]
  472.     labels = _[2]
  473.     return Email(id = '%x' % msgid, fromname = author_name, fromemail = author_email, sendtime = datetime.fromtimestamp(d['time'] // 1000), subject = subject, content = snippet, attachments = attachments, labels = labels)
  474.  
  475.